<?php

declare(strict_types=1);

namespace App\Command;

use App\Constants\CacheKey;
use App\Constants\Consts;
use App\Model\Category;
use Hyperf\Context\ApplicationContext;
use Hyperf\Contract\ConfigInterface;
use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\Command\Annotation\Command;
use Hyperf\Command\Command as HyperfCommand;
use Hyperf\DbConnection\Db;
use Hyperf\Support\Filesystem\Filesystem;
use PHPUnit\Event\Runtime\PHP;
use function Hyperf\Support\env;

require_once(BASE_PATH . '/app/Libs/Helper.php');

/**
 * 批量导入模板文件数据和产品数据到mysql当中
 */
#[Command]
class SaveProductCommand extends HyperfCommand
{
    protected $logger;
    protected $config;

    public function __construct(StdoutLoggerInterface $logger, ConfigInterface $config)
    {
        $this->logger = $logger;
        $this->config = $config;

        // 定义命令行名称
        parent::__construct('save:product');
    }

    public function configure()
    {
        parent::configure();
        $this->setDescription('批量保存产品');
    }

    public function handle()
    {
        ini_set('memory_limit', '-1');
        $startTime = ceil(getMicrotime());
        $fs = new Filesystem();
        $container = ApplicationContext::getContainer();
        $redis = $container->get(\Hyperf\Redis\Redis::class);
        $dbname = env('DB_DATABASE', '');
        $tableJump = 'domain_jump';
        $tableCate = 'categories';
        $tableProduct = 'products';
        $modelCate = new Category();

        // 1.检查路径
        $txtDir = BASE_PATH . '/storage/jkls_txt/';
        $this->line('1.验证目录' . $txtDir . '和分类表');
        if (!$fs->isDirectory($txtDir)) {
            $this->logger->error('没有找到' . $txtDir . '这个目录');
            return \Hyperf\Command\Command::FAILURE;
        }

        // 2.获取产品文件
        $dirs = $fs->directories($txtDir);
        if (empty($dirs)) {
            $this->logger->error($txtDir . '目录下没有产品库目录');
            return \Hyperf\Command\Command::FAILURE;
        }
        $dirCount = count($dirs);
        if ($dirCount < 3 || $dirCount > 5) {
            $this->logger->error($txtDir . '产品库目录只能是3到5个');
            return \Hyperf\Command\Command::FAILURE;
        }
        $files = [];
        $fileCount = 0;
        foreach ($dirs as $dir) {
            $pdb = strtoupper(pathinfo($dir, PATHINFO_BASENAME));
            $temp = $fs->files($dir);
            $files[$pdb] = $temp;
            $fileCount += count($temp);
        }
        if (empty($files)) {
            $this->logger->error($txtDir . '目录下没有产品文件');
            return \Hyperf\Command\Command::FAILURE;
        }
        if ($fileCount <= 70 || $fileCount >= 130) {
            $this->logger->error($txtDir . '目录下的产品文件只能是70-130个之间');
            return \Hyperf\Command\Command::FAILURE;
        }

        // 3.获取已经创建的表
        $res = Db::select("SELECT table_name FROM information_schema.tables WHERE table_schema = '{$dbname}' and table_name='{$tableCate}' limit 1");
        if (empty($res)) {
            $this->logger->error($txtDir . '请先导入分类表');
            return \Hyperf\Command\Command::FAILURE;
        }
        $res = Db::select("SELECT table_name FROM information_schema.tables WHERE table_schema = '{$dbname}' and table_name='{$tableJump}' limit 1");
        if (empty($res)) {
            $sql = file_get_contents(BASE_PATH . '/storage/stub/db_domain_jump.stub');
            Db::select($sql);
        }

        // 4.创建商品DB表和模型
        $this->line('2.创建初始产品表');
        $res = Db::select("SELECT table_name FROM information_schema.tables WHERE table_schema = '{$dbname}' and table_name='{$tableProduct}' limit 1");
        if (empty($res)) {
            $this->genProductTableModel($fs);
        }
        $modelProduct = new \App\Model\Product();
        $tableProductTemp = $tableProduct . '_temp';
        $productCount = $modelProduct::query()->count();
        if ($productCount > 0) {
            $this->logger->error($txtDir . '请先清空商品表数据后再导入');
            return \Hyperf\Command\Command::FAILURE;
        }

        // 5.获取分类slug
        $this->line('3.获取分类slug');
        $res = Db::table($modelCate::TABLE_NAME)->select($modelCate::ID, $modelCate::NAME, $modelCate::SLUGS)->where($modelCate::LEVEL, 1)->get()->toArray();
        $cateids1 = $cateids2 = [];
        foreach ($res as $row) {
            $parentId = intval($row->{$modelCate::ID});
            $name = trim($row->{$modelCate::NAME});
            $cateids1[$name] = $parentId;
            $slugs1[$name] = $row->{$modelCate::SLUGS};

            // 找到对应二级分类的slugs
            $res2 = Db::table($modelCate::TABLE_NAME)->select($modelCate::ID, $modelCate::NAME, $modelCate::PARENT_NAME, $modelCate::SLUGS)
                ->where($modelCate::PARENT_ID, $parentId)
                ->where($modelCate::LEVEL, 2)
                ->get()->toArray();
            foreach ($res2 as $row2) {
                $parentName = trim($row2->{$modelCate::PARENT_NAME});
                $name = trim($row2->{$modelCate::NAME});
                $cateids2[$parentName . '|||' . $name] = $row2->{$modelCate::ID};
                $slugs2[$parentName . '|||' . $name] = $row2->{$modelCate::SLUGS};
            }
        }

        // 6.导入产品数据
        $total = count($files);
        $this->line('5.遍历文件导入数据,共' . $total . '个目录' . $fileCount . '个域名文件');
        $chunk = 500;
        $sj = 0;
        foreach ($files as $pdb => $fileItems) {
            foreach ($fileItems as $file) {
                $filename = $file->getFilename();
                $filepath = $file->getRealPath();
                if (!str_ends_with($filename, '.txt')) {
                    continue;
                }
                $sj += 1;
                $this->line('  开始导入第' . $sj . '个-' . $filename . ',datime=' . date('Y-m-d H:i:s'));

                $domain = str_replace(['jkls_', '.txt', '.upload.tmp'], '', $filename);
                $fh = fopen($filepath, 'r');
                $lid = 0;
                $idata = [];
                while (!feof($fh)) {
                    $line = fgets($fh);
                    if ($line !== false) {
                        $lid++;
                        $arr = explode('|', $line);
                        if (count($arr) < 10) {
                            continue;
                        }
                        $categories = trim($arr[0]);
                        $arrCate = explode('-', $categories);
                        if (empty($arrCate)) {
                            continue;
                        }
                        $cateslug1 = $cateslug2 = '';
                        if (isset($arrCate[5])) {
                            $catename1 = trim($arrCate[5]);
                            $catename2 = trim($arrCate[3]);
                        } elseif (isset($arrCate[3])) {
                            $catename1 = trim($arrCate[3]);
                            $catename2 = trim($arrCate[1]);
                        } elseif (count($arrCate) === 2 && isset($arrCate[1])) {
                            $catename1 = trim($arrCate[1]);
                            $catename2 = 'その他';
                        } else {
                            continue;
                        }
                        $cateid1 = $cateid2 = 0;
                        if (isset($cateids1[$catename1])) {
                            $cateid1 = $cateids1[$catename1];
                            $cateSlugs1 = $slugs1[$catename1];
                            if (str_contains($cateSlugs1, ',')) {
                                $tempArr = explode(',', $cateSlugs1);
                                $cateslug1 = $tempArr[array_rand($tempArr)];
                            } else {
                                $cateslug1 = trim($cateSlugs1, '?');
                            }
                        }
                        if (isset($cateids2[$catename1 . '|||' . $catename2])) {
                            $cateid2 = $cateids2[$catename1 . '|||' . $catename2];
                            $cateSlugs2 = $slugs2[$catename1 . '|||' . $catename2];
                            if (str_contains($cateSlugs2, ',')) {
                                $tempArr = explode(',', $cateSlugs2);
                                $cateslug2 = $tempArr[array_rand($tempArr)];
                            } else {
                                $cateslug2 = trim($cateSlugs2, '?');
                            }
                        }
                        if (empty($cateslug1) || empty($cateslug2) || empty($cateid1) || empty($cateid2)) {
                            var_dump($arr, $filename, $catename1 . '|||' . $catename2, '$cateid1=' . $cateid1, '$cateid2=' . $cateid2);
                            $this->logger->error('分类找不到slugs，请确认是否添加了');
                            continue;
                        }
                        $price = filterZero($arr[5]);
                        $mprice = filterZero($arr[4]);

                        if ($price > 5000 && $mprice > 5000) {
                            $idata[] = [
                                $modelProduct::DOMAIN => $domain,
                                $modelProduct::PRODUCT_ID => intval($arr[1] ?? 0),
                                $modelProduct::PRODUCT_DB => $pdb,
                                $modelProduct::CATE_NAME1 => $catename1,
                                $modelProduct::CATE_NAME2 => $catename2,
                                $modelProduct::CATE_SLUG1 => $cateslug1,
                                $modelProduct::CATE_SLUG2 => $cateslug2,
                                $modelProduct::CATE_ID1 => $cateid1,
                                $modelProduct::CATE_ID2 => $cateid2,
                                $modelProduct::LOGO => $arr[3],
                                $modelProduct::PRICE => $price,
                                $modelProduct::MARKET_PRICE => $mprice,
                                $modelProduct::TITLE => $arr[6],
                                $modelProduct::INTRO => $arr[7],
                                $modelProduct::SEO_TITLE => $arr[8] ?? '',
                                $modelProduct::SEO_DESC => $arr[10] ?? '',
                                $modelProduct::IMAGES => '',
                            ];
                            if ($lid % $chunk === 0) {
                                Db::table($tableProduct)->insert($idata);
                                $idata = [];
                            }
                        }
                    }
                }
                if (!empty($idata)) {
                    Db::table($tableProduct)->insert($idata);
                }
                fclose($fh);
            }
        }

        $this->line("6.打乱产品的顺序后导入到临时表");
        $productCount = Db::table($tableProduct)->count();
        $arr = range(1, $productCount);
        shuffle($arr);
        $str = '';
        $inserFields = '`domain`,`product_id`,`product_db`,`cate_name1`,`cate_name2`,`cate_slug1`,`cate_slug2`,`cate_id1`,`cate_id2`,`logo`,`market_price`,`price`,`title`,`intro`,`seo_title`,`seo_desc`,`images`';
        for ($i = 1; $i <= $productCount; $i++) {
            $j = $i - 1;
            $str .= $arr[$j] . ',';
            if ($i % $chunk === 0) {
                $str = rtrim($str, ',');
                $sql = "insert into {$tableProductTemp} ($inserFields) select $inserFields from {$tableProduct} where id in(" . $str . ") ORDER BY FIELD(id, $str)";
                Db::insert($sql);
                $str = '';
                $this->line('  保存到临时表，last_id=' . $i . ',datime=' . date('Y-m-d H:i:s'));
            }
        }
        if (!empty($str)) {
            $str = rtrim($str, ',');
            $sql = " insert into {$tableProductTemp}($inserFields) select $inserFields from {$tableProduct} where id in(" . $str . ") ORDER BY FIELD(id, $str)";
            Db::insert($sql);
        }

        $chunk2 = 40000;
        $this->line('7.生成产品文件，共' . $productCount . '个' . ceil($productCount / $chunk2) . '批');
        $productDir = BASE_PATH . '/storage/products/';
        if (!$fs->isDirectory($productDir)) {
            $fs->makeDirectory($productDir, 0755, true);
        }
        // 删除data/products中的文件
        $fs->cleanDirectory($productDir);
        $pdirnum = env('PRODUCTDIR_NUM');
        for ($i = 0; $i < $productCount; $i = $i + $chunk2) {
            $ij = $i + 1;
            $this->line('  开始生成第' . $ij . '批,起始=' . $i);
            $sql = "select * from {$tableProductTemp} limit {$i},{$chunk2}";
            $res = Db::select($sql);
            foreach ($res as $row) {
                $row = (array)$row;
                $subDir = ceil($row['id'] / $pdirnum);
                $productPath = $productDir . $subDir . '/';
                if (!file_exists($productPath)) {
                    mkdir($productPath, 0755, true);
                }
                $productTxt = $productPath . $row['id'] . '.txt';
                $seoDesc = !empty($row['seo_desc']) ? $row['seo_desc'] : '-';
                $content = $row['domain'] . '|||' . $row['product_id'] . '|||' . $row['product_db'] . '|||' . $row['cate_name1'] . '|||' . $row['cate_name2'] . '|||' . $row['cate_slug1'] . '|||' . $row['cate_slug2'] . '|||' . $row['cate_id1'] . '|||' . $row['cate_id2'] . '|||' . $row['logo'] . '|||' . $row['market_price'] . '|||' . $row['price'] . '|||' . $row['title'] . '|||' . $row['intro'] . '|||' . $row['seo_title'] . '|||' . $seoDesc;
                $fs->put($productTxt, $content);

                // 将logo、标题和价格存到redis中(商品详情中用到)
                $ck4 = sprintf(CacheKey::PK, $row['id']);
                $redis->set($ck4, $row['title'] . '|||' . $row['market_price'] . '|||' . $row['logo']);
            }
        }
        $pPath = BASE_PATH . Consts::PRODUCT_COUNT_PATH;
        $fs->put($pPath, $productCount);
        $redis->set(CacheKey::PRODUCT_MAXID, $productCount);

        $this->line('7.删除临时表并移动为产品表');
        Db::select("DROP TABLE {$tableProduct}");
        Db::select("RENAME TABLE {$tableProductTemp} TO {$tableProduct}");

        $this->line('8.产生domains文件');
        $sql = "select distinct domain from `$tableProduct`";
        $res = Db::select($sql);
        $mydomains = [];
        foreach ($res as $row) {
            $mydomains[] = $row->{$modelProduct::DOMAIN};
        }
        $mydomains = join('|', $mydomains);
        $dPath = BASE_PATH . Consts::DOMAINS_PATH;
        $fs->put($dPath, $mydomains);

        $endTime = ceil(getMicrotime());
        $huafei = $endTime - $startTime;

        $this->line('产品已经全部入库完成，共花费' . $huafei . '秒，程序退出');
        return \Hyperf\Command\Command::SUCCESS;
    }

    /**
     * 自动创建DB表
     */
    private function genProductTableModel($fs)
    {
        $sql1 = file_get_contents(BASE_PATH . '/storage/stub/db_product.stub');
        Db::select($sql1);

        $sql2 = file_get_contents(BASE_PATH . '/storage/stub/db_product.stub');
        $sql2 = str_replace('`products`', '`products_temp`', $sql2);
        Db::select($sql2);

        $dir = BASE_PATH . "/app/Model";
        $path = $dir . DIRECTORY_SEPARATOR . 'Product.php';
        if (!$fs->isDirectory($dir)) {
            $fs->makeDirectory(
                path: $dir,
                recursive: true
            );
        }
        if (!file_exists($path)) {
            $model = file_get_contents(BASE_PATH . "/storage/stub/model_product.stub");
            $fs->put($path, $model);
        }
        return true;
    }
}
